package cn.jingyuan.owl.utils.core.annotation;

import cn.jingyuan.owl.utils.core.ArrayUtils;
import cn.jingyuan.owl.utils.core.ReflectUtils;
import cn.jingyuan.owl.utils.core.exceptions.UtilsException;
import cn.jingyuan.owl.utils.core.lang.Filter;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 注解工具类 <br>
 * 快速获取注解对象、注解值等工具封装。
 */
public class AnnotationUtils {

    /**
     * 将指定的被注解的元素转换为组合注解元素
     *
     * @param annotationEle 注解元素
     *
     * @return 组合注解元素
     */
    public static CombinationAnnotationElement toCombination(AnnotatedElement annotationEle) {
        if (annotationEle instanceof CombinationAnnotationElement) {
            return (CombinationAnnotationElement) annotationEle;
        }
        return new CombinationAnnotationElement(annotationEle);
    }

    /**
     * 获取指定注解
     *
     * @param annotationEle {@link AnnotatedElement} 可以是 Class、Method、Field、Constructor、ReflectPermission
     * @param isToCombination 是否为转换为组合注解
     *
     * @return 注解对象
     */
    public static Annotation[] getAnnotations(AnnotatedElement annotationEle, boolean isToCombination) {
        if (null == annotationEle) {
            return null;
        }

        return (isToCombination ? toCombination(annotationEle) : annotationEle).getAnnotations();
    }

    /**
     * 获取指定注解
     *
     * @param <A> 注解类型
     * @param annotationEle {@link AnnotatedElement} 可以是 Class、Method、Field、Constructor、ReflectPermission
     * @param annotationType 注解类型
     *
     * @return 注解对象
     */
    public static <A extends Annotation> A getAnnotation(AnnotatedElement annotationEle, Class<A> annotationType) {
        return (null == annotationEle) ? null : toCombination(annotationEle).getAnnotation(annotationType);
    }

    /**
     * 获取指定注解默认值<br>
     * 如果无指定的属性方法返回 null
     *
     * @param <T> 注解值类型
     * @param annotationEle {@link AccessibleObject} 可以是 Class、Method、Field、Constructor、ReflectPermission
     * @param annotationType 注解类型
     *
     * @return 注解对象
     *
     * @throws UtilsException 调用注解中的方法时执行异常
     */
    public static <T> T getAnnotationValue(AnnotatedElement annotationEle, Class<? extends Annotation> annotationType) {
        return getAnnotationValue(annotationEle, annotationType, "value");
    }

    /**
     * 获取指定注解属性的值<br>
     * 如果无指定的属性方法返回 null
     *
     * @param <T> 注解值类型
     * @param annotationEle {@link AccessibleObject} 可以是 Class、Method、Field、Constructor、ReflectPermission
     * @param annotationType 注解类型
     * @param propertyName 属性名，例如注解中定义了 name()方法，则 此处传入 name
     *
     * @return 注解对象
     *
     * @throws UtilsException 调用注解中的方法时执行异常
     */
    public static <T> T getAnnotationValue(AnnotatedElement annotationEle, Class<? extends Annotation> annotationType, String propertyName) {
        final Annotation annotation = getAnnotation(annotationEle, annotationType);
        if (null == annotation) {
            return null;
        }

        final Method method = ReflectUtils.getMethodOfObj(annotation, propertyName);
        if (null == method) {
            return null;
        }
        return ReflectUtils.invoke(annotation, method);
    }

    /**
     * 获取指定注解中所有属性值<br>
     * 如果无指定的属性方法返回 null
     *
     * @param annotationEle {@link AnnotatedElement} 可以是 Class、Method、Field、Constructor、ReflectPermission
     * @param annotationType 注解类型
     *
     * @return 注解对象
     *
     * @throws UtilsException 调用注解中的方法时执行异常
     */
    public static Map<String, Object> getAnnotationValueMap(AnnotatedElement annotationEle, Class<? extends Annotation> annotationType) {
        final Annotation annotation = getAnnotation(annotationEle, annotationType);
        if (null == annotation) {
            return null;
        }

        final Method[] methods = ReflectUtils.getMethods(annotationType, new Filter<Method>() {

            @Override
            public boolean accept(Method t) {
                if (ArrayUtils.isEmpty(t.getParameterTypes())) {
                    // 只读取无参方法
                    final String name = t.getName();
                    // 跳过自有的几个方法
                    return (!"hashCode".equals(name)) && (!"toString".equals(name)) && (!"annotationType".equals(name));
                }
                return false;
            }
        });

        final HashMap<String, Object> result = new HashMap<>(methods.length, 1);
        for (Method method : methods) {
            result.put(method.getName(), ReflectUtils.invoke(annotation, method));
        }
        return result;
    }

    /**
     * 获取注解类的保留时间，可选值 SOURCE（源码时），CLASS（编译时），RUNTIME（运行时），默认为 CLASS
     *
     * @param annotationType 注解类
     *
     * @return 保留时间枚举
     */
    public static RetentionPolicy getRetentionPolicy(Class<? extends Annotation> annotationType) {
        final Retention retention = annotationType.getAnnotation(Retention.class);
        if (null == retention) {
            return RetentionPolicy.CLASS;
        }
        return retention.value();
    }

    /**
     * 获取注解类可以用来修饰哪些程序元素，如：TYPE，METHOD，CONSTRUCTOR，FIELD，PARAMETER 等。
     *
     * @param annotationType 注解类
     *
     * @return 注解修饰的程序元素数组
     */
    public static ElementType[] getTargetType(Class<? extends Annotation> annotationType) {
        final Target target = annotationType.getAnnotation(Target.class);
        if (null == target) {
            return new ElementType[]{
                ElementType.TYPE,
                ElementType.FIELD,
                ElementType.METHOD,
                ElementType.PARAMETER,
                ElementType.CONSTRUCTOR,
                ElementType.LOCAL_VARIABLE,
                ElementType.ANNOTATION_TYPE,
                ElementType.PACKAGE
            };
        }
        return target.value();
    }

    /**
     * 是否会保存到 Javadoc 文档中
     *
     * @param annotationType 注解类
     *
     * @return 是否会保存到 Javadoc 文档中
     */
    public static boolean isDocumented(Class<? extends Annotation> annotationType) {
        return annotationType.isAnnotationPresent(Documented.class);
    }

    /**
     * 是否可以被继承，默认为 false
     *
     * @param annotationType 注解类
     *
     * @return 是否会保存到 Javadoc 文档中
     */
    public static boolean isInherited(Class<? extends Annotation> annotationType) {
        return annotationType.isAnnotationPresent(Inherited.class);
    }

}
